Skip to content

feat(rules): block or ask first before Claude Code and Cursor run a tool - #76

Merged
0xCardinal merged 1 commit into
mainfrom
feat/hook-blocking
Nov 4, 2026
Merged

0xCardinal merged 1 commit into
mainfrom
feat/hook-blocking

Conversation

@0xCardinal

Copy link
Copy Markdown
Member

Rules can now stop a tool call instead of only recording it. A rule set to Block or Ask first is checked from the agent's pre-tool hook, and the agent either refuses the call or asks the person, showing the rule's reason.

Collector

  • Blocking is on (ENFORCEMENT_ENABLED). A blocking rule must use only details known before the tool runs, and its agent scope must include one that can block: Claude Code and Cursor for now (ENFORCEMENT_SUPPORT).
  • POST /v1/decide/{source} answers from the in-memory rule cache and never waits on a DB write (p50 about 2 ms on a fresh DB, 12 ms on the live one). Blocks and asks are written to the audit log off-thread.
  • PUT /v1/enforcement pauses every blocking rule at once.
  • cot's own bridge commands are never blocked.

Bridge

  • Claude PreToolUse and Cursor preToolUse ask /v1/decide with a 0.5 s timeout and allow the call on any failure.
  • The check ignores the collector-down backoff. Live testing showed that one slow ingest post marked the collector down for 10 s and let blocked calls through.
  • Decisions map onto Claude's permissionDecision and Cursor's {permission, user_message, agent_message}. Cursor's shape was confirmed from its bundled preToolUseResponse validator. The stored event records cot_decision.

Dashboard

  • The rule sentence opens with Tell me when / Ask me first when / Block it when. Choosing a blocking option hides what can't be decided before a run: spend, "and it fails", repeat limits, and agents that can't block.
  • The rules list shows block / ask first chips and a Blocking on switch. Previews read "Would have blocked".

Testing

  • Backend: 757 tests pass, including new decide and bridge reply tests. Ruff is clean. tsc and 43 frontend tests pass.
  • End to end on a scratch collector: Claude deny and ask, Cursor deny, Codex left alone, pause, and collector down all behaved as expected.
  • Live in Claude Code: a WebFetch to a domain covered by a Block rule was refused with the rule's reason.

Not in this PR: Codex, OpenCode and Hermes blocking, and the per-agent "Blocked actions" panel (phase 2).

🤖 Generated with Claude Code

Rules can now stop a tool call instead of only recording it. A rule whose
action is "deny" (Block) or "ask" (Ask first) is checked from the agent's
pre-tool hook, and the agent either refuses the call or asks the person,
showing the rule's reason.

Collector
- ENFORCEMENT_ENABLED is on. Blocking rules must be match rules over
  details known before the tool runs, scoped to an agent that can block
  (Claude Code and Cursor for now, ENFORCEMENT_SUPPORT).
- POST /v1/decide/{source} answers from the in-memory rule cache and never
  waits on a DB write. Blocks and asks go to the audit log off-thread.
- PUT /v1/enforcement pauses every blocking rule at once.
- cot's own bridge commands are never blocked.

Bridge
- Pre-tool hooks (Claude PreToolUse, Cursor preToolUse) ask /v1/decide with
  a 0.5s timeout and allow the call on any failure. The check ignores the
  collector-down backoff: one slow ingest post used to mark the collector
  down and let blocked calls through for ten seconds.
- The decision maps onto Claude's permissionDecision and Cursor's
  {permission, user_message, agent_message} replies, and is recorded on
  the stored event as cot_decision.

Dashboard
- The rule sentence opens with "Tell me when / Ask me first when / Block it
  when"; blocking hides what can't be decided before a run (spend, "and it
  fails", repeat limits, agents that can't block).
- The rules list shows block / ask first chips and a Blocking on switch;
  previews read "Would have blocked".
@0xCardinal
0xCardinal merged commit 2242de2 into main Nov 4, 2026
7 checks passed
@0xCardinal
0xCardinal deleted the feat/hook-blocking branch November 4, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant